You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This PR keeps the app on a single GAE service while reducing startup cost and cleaning up deploy/dev ergonomics.
Main changes:
keeps API, /ogcapi, and /admin in one service
lazy-loads admin on first /admin access instead of importing/mounting it at app startup
keeps GCS imports lazy on asset paths
removes the leftover split-service config and entrypoints
adds startup/request timing logs for cold vs warm behavior
hardens GAE deploy cleanup and keeps warmup/business-hours scaling support
adds a follow-up Alembic migration to rebuild the water elevation materialized view for DBs that already recorded the earlier revision but still have the old schema
Why
The previous refactor proved that cold-start cost was dominated by import-time work. Splitting admin and pygeoapi into separate services reduced that cost, but it also added routing, Docker, and deploy complexity that is not worth it for current usage.
This branch takes the lower-complexity version:
stay single-service
keep the low-risk lazy-loading wins
move admin off the main startup path
keep GAE warmup/scaling improvements
preserve local Docker usability
Key changes
Startup and runtime
core/factory.py
uses shared app factory
mounts pygeoapi in the main app
loads session middleware only when SESSION_SECRET_KEY is present
enables lazy admin initialization
core/initializers.py
splits API route registration from admin setup
mounts admin only on first /admin request
returns a clear 503 for /admin when SESSION_SECRET_KEY is not configured
core/app.py
adds /_ah/warmup
logs instance startup complete
logs per-request timing with request_kind=cold|warm
Lazy imports / import-path cleanup
api/asset.py
lazy-imports GCS helpers at use sites
services/gcs_helper.py
moves Google client imports inside functions
services/asset_helper.py
avoids runtime import of google.cloud.storage for typing only
services/env.py
new lightweight env helper module
db/engine.py
alembic/env.py
now import get_bool_env from the lightweight env module instead of the heavier utility module
The largest remaining cold-start cost is now pygeoapi import/setup, not admin. If more startup reduction is needed later, that is the next area to target.
jirhiker
changed the title
Fix unused imports and reinforce cleanup instructions
Reduce cold-start overhead and simplify single-service deployment
Mar 10, 2026
remove_asset passes asset.uri to gcs_remove, but gcs_remove calls bucket.blob(...), which expects a blob/object name (the storage_path), not a full URL. For assets created through the upload flow, this targets a non-existent object key and makes /asset/{asset_id}/remove fail to delete the real file (or return an error) in production cleanup scenarios.
database_error_handler unconditionally raises PydanticStyleException(detail=[detail]), but detail is only assigned inside one specific Postgres message match. Any other ProgrammingError path will hit an UnboundLocalError, masking the real database failure and returning an unexpected 500 instead of a controlled API error.
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR keeps the app on a single GAE service while reducing startup cost and cleaning up deploy/dev ergonomics.
Main changes:
/ogcapi, and/adminin one service/adminaccess instead of importing/mounting it at app startupWhy
The previous refactor proved that cold-start cost was dominated by import-time work. Splitting admin and pygeoapi into separate services reduced that cost, but it also added routing, Docker, and deploy complexity that is not worth it for current usage.
This branch takes the lower-complexity version:
Key changes
Startup and runtime
core/factory.pySESSION_SECRET_KEYis presentcore/initializers.py/adminrequest503for/adminwhenSESSION_SECRET_KEYis not configuredcore/app.py/_ah/warmupinstance startup completerequest_kind=cold|warmLazy imports / import-path cleanup
api/asset.pyservices/gcs_helper.pyservices/asset_helper.pygoogle.cloud.storagefor typing onlyservices/env.pydb/engine.pyalembic/env.pyget_bool_envfrom the lightweight env module instead of the heavier utility moduleDeployment / App Engine
.github/app.template.yamlgunicorn -w 1.github/workflows/CD_staging.yml.github/workflows/CD_production.ymlLocal Docker dev
docker-compose.ymldb+app:8000entrypoint.shRUN_MIGRATIONSdefaults totrueMaterialized view fix
alembic/versions/o8b9c0d1e2f3_rebuild_water_elevation_materialized_view.pyogc_water_elevation_wellswith the expected feet-normalized schematests/integration/test_alembic_migrations.pyNotes
SESSION_SECRET_KEYis no longer required just to boot the main API locally./admin./adminreturns a clear503instead of breaking main app startup.service_account: "${CLOUD_SQL_USER}.gserviceaccount.com"Validation
Ran:
pytest tests/test_lazy_admin.py tests/test_request_timing.py tests/test_ogc.py tests/test_admin_views.py tests/integration/test_alembic_migrations.py tests/test_cli_commands.py -qResult:
55 passed, 3 skippedAlso verified:
import mainworks withSESSION_SECRET_KEYunsetFollow-up
The largest remaining cold-start cost is now pygeoapi import/setup, not admin. If more startup reduction is needed later, that is the next area to target.